// Copyright 2009-2023 NTESS. Under the terms
// of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Copyright (c) 2009-2023, NTESS
// All rights reserved.
//
// Portions are copyright of other developers:
// See the file CONTRIBUTORS.TXT in the top level directory
// of the distribution for more information.
//
// This file is part of the SST software package. For license
// information, see the LICENSE file in the top level directory of the
// distribution.

/**
 * @file cuda_runtime_api.h
 * @author Weili An (an107@purdue.edu)
 * @brief Test lib header for vanadis calling CUDA api
 * @version 0.1
 * @date 2022-09-22
 *
 */

#ifndef __CUDA_RUNTIME_API_H__
#define __CUDA_RUNTIME_API_H__

#include <stdbool.h>
#include <stdexcept>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <iostream>
#include <fstream>

inline std::string ADD_QUOTES(std::string src) {
  return "\"" + src + "\"";
} 

class HexFormatter {
public:
  HexFormatter(std::ostream& File) : File(File) {}
  void operator()(uint8_t num){
    File << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(num);
  }
private:
  std::ostream& File;
};

class DecimalFormatter {
public:
  DecimalFormatter(std::ostream& File) : File(File) {}
  void operator()(uint8_t num) {
    // Avoid printing non-printable or garbled characters.
    if (num >= 32 && num <= 126) {
      File << static_cast<char>(num);
    }
  }
private:
  std::ostream& File;
};

#define CryptoTypesNum 4
enum OP_TYPE {
  INPUT_MULTI_SHA1,
  INPUT_MULTI_MD5,
  INPUT_MULTI_SM3,
  INPUT_MULTI_AES,

  INPUT_CRACK_SHA1,
  INPUT_CRACK_MD5,
  INPUT_CRACK_SM3,
  INPUT_CRACK_AES,

  OUTPUT_MULTI_SHA1,
  OUTPUT_MULTI_MD5,
  OUTPUT_MULTI_SM3,
  OUTPUT_MULTI_AES,
  
  OUTPUT_CRACK_SHA1,
  OUTPUT_CRACK_MD5,
  OUTPUT_CRACK_SM3,
  OUTPUT_CRACK_AES,

  EXTRA_CRACK_SHA1,
  EXTRA_CRACK_MD5,
  EXTRA_CRACK_SM3,
  EXTRA_MULTI_CRACK_AES,
};

enum cudaMemcpyKind {
  cudaMemcpyHostToHost = 0,     /**< Host   -> Host */
  cudaMemcpyHostToDevice = 1,   /**< Host   -> Device */
  cudaMemcpyDeviceToHost = 2,   /**< Device -> Host */
  cudaMemcpyDeviceToDevice = 3, /**< Device -> Device */
  cudaMemcpyDefault =
      4 /**< Direction of the transfer is inferred from the pointer values.
           Requires unified virtual addressing */
};

enum cudaError {
  cudaSuccess = 0,
  cudaErrorMissingConfiguration = 1,
  cudaErrorMemoryAllocation = 2,
  cudaErrorInitializationError = 3,
  cudaErrorLaunchFailure = 4,
  cudaErrorPriorLaunchFailure = 5,
  cudaErrorLaunchTimeout = 6,
  cudaErrorLaunchOutOfResources = 7,
  cudaErrorInvalidDeviceFunction = 8,
  cudaErrorInvalidConfiguration = 9,
  cudaErrorInvalidDevice = 10,
  cudaErrorInvalidValue = 11,
  cudaErrorInvalidPitchValue = 12,
  cudaErrorInvalidSymbol = 13,
  cudaErrorMapBufferObjectFailed = 14,
  cudaErrorUnmapBufferObjectFailed = 15,
  cudaErrorInvalidHostPointer = 16,
  cudaErrorInvalidDevicePointer = 17,
  cudaErrorInvalidTexture = 18,
  cudaErrorInvalidTextureBinding = 19,
  cudaErrorInvalidChannelDescriptor = 20,
  cudaErrorInvalidMemcpyDirection = 21,
  cudaErrorAddressOfConstant = 22,
  cudaErrorTextureFetchFailed = 23,
  cudaErrorTextureNotBound = 24,
  cudaErrorSynchronizationError = 25,
  cudaErrorInvalidFilterSetting = 26,
  cudaErrorInvalidNormSetting = 27,
  cudaErrorMixedDeviceExecution = 28,
  cudaErrorCudartUnloading = 29,
  cudaErrorUnknown = 30,
  cudaErrorNotYetImplemented = 31,
  cudaErrorMemoryValueTooLarge = 32,
  cudaErrorInvalidResourceHandle = 33,
  cudaErrorNotReady = 34,
  cudaErrorInsufficientDriver = 35,
  cudaErrorSetOnActiveProcess = 36,
  cudaErrorNoDevice = 38,
  cudaErrorStartupFailure = 0x7f,
  cudaErrorApiFailureBase = 10000
};

struct dim3 {
  unsigned int x, y, z;
};

typedef enum cudaError cudaError_t;
typedef struct dim3 dim3;

extern "C" cudaError_t cudaSetDevice(int device) { return cudaSuccess; };

extern "C" cudaError_t cudaMalloc(void **devPtr, uint64_t size) {
  return cudaSuccess;
};

extern "C" cudaError_t cudaMemcpy(uint64_t dst, uint64_t src, uint64_t count,
                                  enum cudaMemcpyKind kind) {
  return cudaSuccess;
};

// Cuda Configure call
extern "C" cudaError_t cudaConfigureCall(dim3 gridDim, dim3 blockDim,
                                         uint64_t sharedMem) {
  return cudaSuccess;
};

// Cuda Setup argument
extern "C" cudaError_t cudaSetupArgument(const void *arg, size_t size,
                                         size_t offset) {
  return cudaSuccess;
};

extern "C" cudaError_t cudaLaunch(uint64_t func) { return cudaSuccess; };

extern "C" unsigned int __cudaRegisterFatBinary(char file_name[256]) {
  return 0;
};

// TODO: How to get the deviceFun name automatically?
// TODO: Requires parsing the binary?
extern "C" void __cudaRegisterFunction(uint64_t fatCubinHandle,
                                       uint64_t hostFun,
                                       char deviceFun[256]) {};

extern "C" int ariel_msg_send(uint32_t src, uint32_t dst, void *buf_p,
                              uint64_t count, uint64_t tag) {};
extern "C" int ariel_msg_recv(uint32_t src, uint32_t dst, void *buf_p,
                              uint64_t count, uint64_t tag, int nb) {};

extern "C" unsigned int getCPUID() {
  const char *env_id = std::getenv("CPUID");
  if (env_id != nullptr) {
    return std::stoi(env_id);
  }
  return 0;
}

extern "C" void printScheduleInfo(
    unsigned int groupId, intptr_t byte_shift, intptr_t size,
    unsigned char *data_allocated, unsigned char *data_aligned,
    intptr_t data_offset, intptr_t data_size, intptr_t data_stride,
    unsigned int *devices_allocated, unsigned int *devices_aligned,
    intptr_t devices_offset, intptr_t devices_size, intptr_t devices_stride,
    float *ratios_allocated, float *ratios_aligned, intptr_t ratios_offset,
    intptr_t ratios_size, intptr_t ratios_stride);

#endif
